// GrCamera.h: interface for the CGrCamera class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_GRCAMERA_H__4AA26B3C_6FD9_4573_9A4A_B677E9F852D0__INCLUDED_)
#define AFX_GRCAMERA_H__4AA26B3C_6FD9_4573_9A4A_B677E9F852D0__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "carnivalbuilder.h"
#include <GL/glu.h>


//! Class that defines a simple camera model we can use with the mouse.

//! The CGrCamera class can be used in application to allow the mouse to
//! change the viewpoint by changes to pitch, yaw, pan, tilt, and other
//! Common camera operations.
//! See <a href="https://www.cse.msu.edu/~cse472/secure/installcgrcamera.html">
//! Installing and Using CGrCamera for details.</a>

class CSgCamera :
	public CarnivalBuilder
{
public:
	CSgCamera();
	virtual ~CSgCamera();

    //! Number of mouse buttons supported
    static const int NumButtons = 3;

	double CameraDistance() const;
	void Gravity(bool p_gravity);
	void DollyCenter(double x, double y, double z);
	void DollyCamera(double x, double y, double z);
	void Dolly(double x, double y, double z);
	void Pitch(double d);
	void Yaw(double d);
	void Roll(double d);
	void Tilt(double d);
	void Pan(double d);
	void Set3dv(const double *p_eye, const double *p_center, const double *p_up);
	void Set(double p_eyex, double p_eyey, double p_eyez, double p_cenx, double p_ceny, double p_cenz, double p_upx, double p_upy, double p_upz);
	void SetEye(double p_eyex, double p_eyey, double p_eyez);
	void SetCenter(double p_cenx, double p_ceny, double p_cenz);
	void SetUp(double p_upx, double p_upy, double p_upz);
    void MoveIn(double z);
	void Move(double d);

    void FieldOfView(double f) {m_fieldofview = f;}
    double FieldOfView() const {return m_fieldofview;}

    const double *Eye() const {return m_eye;}
    const double *Center() const {return m_center;}
    const double *Up() const {return m_up;}

    double EyeX() const {return m_eye[0];}
    double EyeY() const {return m_eye[1];}
    double EyeZ() const {return m_eye[2];}
    double CenterX() const {return m_center[0];}
    double CenterY() const {return m_center[1];}
    double CenterZ() const {return m_center[2];}
    double UpX() const {return m_up[0];}
    double UpY() const {return m_up[1];}
    double UpZ() const {return m_up[2];}
    double ZNear() const {return CameraDistance() * (1 - m_zRange);}
    double ZFar() const {return CameraDistance() * (1 + m_zRange);}

    bool Gravity() const {return m_gravity;}

    enum eMouseMode {PANTILT=10, ROLLMOVE=11, PITCHYAW=12, DOLLYXY=13, MOVE=14};

    void MouseMode(eMouseMode m, int b=1);
    eMouseMode MouseMode(int b=1) const {return m_mousemode[b-1];}
    void MouseDown(int x, int y, int button=1);
	void MouseMove(int x, int y);

	void glutKeyboardFunc(void (*func) (unsigned char key, int x, int y)); 
	void processNormalKeys(unsigned char key, int x, int y);

    inline void gluLookAt() {::gluLookAt(m_eye[0], m_eye[1], m_eye[2], 
                                  m_center[0], m_center[1], m_center[2],
                                  m_up[0], m_up[1], m_up[2]);}

    void Apply(int width, int height, bool noidentity=false);

private:
	double          m_up[4];
	double          m_center[4];
	double          m_eye[4];

	double          m_fieldofview;

    eMouseMode      m_mousemode[NumButtons];
	void DollyHelper(double m[4][4], double x, double y, double z);
	void ComputeFrame();
    bool     m_gravity;
    double m_zRange;

    // Last mouse press
	int             m_mousey;
	int             m_mousex;
    int             m_mouseButton;      // 0 for non pressed, 1-NumButtons otherwise

    // The camera frame.
    double          m_camerax[3];
    double          m_cameray[3];
    double          m_cameraz[3];

    void RotCamera(double m[4][4]);
    void UnRotCamera(double m[4][4]);
    void RotCameraX(double m[4][4], double a);
    void RotCameraY(double m[4][4], double a);
    void RotCameraZ(double m[4][4], double a);
};

#endif // !defined(AFX_GRCAMERA_H__4AA26B3C_6FD9_4573_9A4A_B677E9F852D0__INCLUDED_)
